package com.seepine.auth.interceptor;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.seepine.auth.annotation.Secret;
import com.seepine.auth.entity.AuthProperties;
import com.seepine.auth.exception.AuthException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.PostConstruct;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

/**
 * SecretInterceptor
 *
 * @author seepine
 * @since 1.3.0
 */
public class SecretInterceptor implements HandlerInterceptor {

    private static final String KEY_ALGORITHM = "AES";
    private static final int SECRET_IV_LENGTH = 16;
    @Autowired
    AuthProperties authProperties;

    private AES aes;

    @PostConstruct
    public void init() throws AuthException {
        if (StrUtil.isBlank(authProperties.secretKey())) {
            throw new AuthException("secretKey is blank");
        }
        if (StrUtil.isBlank(authProperties.secretIv())) {
            throw new AuthException("secretIv is blank");
        }
        if (authProperties.secretIv().length() != SECRET_IV_LENGTH) {
            throw new AuthException("secretIv length must 16");
        }
        aes = new AES(Mode.CBC, Padding.NoPadding, new SecretKeySpec(authProperties.secretKey().getBytes(), KEY_ALGORITHM),
                new IvParameterSpec(authProperties.secretIv().getBytes()));
    }

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        if (hasAnnotation(handler)) {
            String timeSecret = httpServletRequest.getHeader(authProperties.secretHeader());
            if (StrUtil.isBlank(timeSecret)) {
                httpServletResponse.setStatus(510);
                throw new AuthException("not find " + authProperties.secretHeader());
            }
            long originTime;
            try {
                String result = aes.decryptStr(Base64.decode(timeSecret.getBytes(StandardCharsets.UTF_8))).trim();
                originTime = Long.parseLong(result);
            } catch (Exception e) {
                e.printStackTrace();
                httpServletResponse.setStatus(511);
                throw new AuthException("decrypt secret fail");
            }
            long nowTime = System.currentTimeMillis();
            long difference = Math.abs(nowTime - originTime);
            if (difference <= authProperties.secretTimeout()) {
                return true;
            }
            httpServletResponse.setStatus(512);
            throw new AuthException("invalid " + authProperties.secretHeader());
        }
        return true;
    }

    /**
     * 方法上有注解或类上有注解
     *
     * @param handler handler
     * @return boolean
     */
    private boolean hasAnnotation(Object handler) {
        try {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            return method.isAnnotationPresent(Secret.class) || method.getDeclaringClass().isAnnotationPresent(Secret.class);
        } catch (Exception ignored) {
            return false;
        }
    }
}
